
import sys
try:
     import xml.etree.cElementTree as ET
except ImportError:
     import xml.etree.ElementTree as ET

from PyQt5.QtWidgets import (QApplication, QWidget, QFrame, QMainWindow, QDialog, QMessageBox,
        QGroupBox, QBoxLayout, QHBoxLayout, QVBoxLayout, QFormLayout, QGridLayout, QDialogButtonBox,
        QPushButton, QLabel, QLineEdit, QTextEdit, QRadioButton, QComboBox, QMenu, QMenuBar,
        QTableWidget, QHeaderView, QTableWidgetItem)
from PyQt5.QtGui import QPixmap, QPalette, QBrush, QIcon
from PyQt5.QtCore import Qt

import py3func

LOG = py3func.make_logging(__file__)
TABLE_ROW_NUM = 20

def dict_value(dataDict, key, not_found_strategy=None):
    """ return a value of the key or raise exception """
    if key not in dataDict:
        raise Exception("Not Found the item[%s]" % key)
    else:
        return dataDict[key]


class EditViewer(QFrame):
    def __init__(self, xml_path):
        super().__init__()

        self.make_window()
        self.make_layout()
        self.make_widgets()
        self.load_data_file(xml_path)

        self.show()

    def make_window(self):
        self.setGeometry(10,40,800,600)

    def make_layout(self):
        self.layout = QVBoxLayout()
        self.layout.setSpacing(20)  # 布局中的控件间距
        self.layout_title = QGridLayout()
        self.layout_table = QVBoxLayout()
        self.layout_bottom = QHBoxLayout()
        self.layout.addLayout(self.layout_title)
        self.layout.addLayout(self.layout_table)
        self.layout.addLayout(self.layout_bottom)
        self.setLayout(self.layout)

    def make_widgets(self):
        header = self.make_table([  ("index"   , 1),
                                    ("id"      , 2),
                                    ("tag"     , 2),
                                    ("text"    , 3),
                                    ("pos_x"   , 1),
                                    ("pos_y"   , 1),
                                    ("width"   , 1),
                                    ("height"  , 1),
                                    ("image"   , 3)
                                ])
        header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(2, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(3, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(4, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(5, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(6, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(7, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(8, QHeaderView.Stretch)

    def make_table(self, list_header_attrib):
        """ return the horizontal-header.
            if u need, override the method """
        num_column = len(list_header_attrib)

        # self.column_width_rate = [x[1] for x in list_header_attrib]
        self.table = QTableWidget(TABLE_ROW_NUM, num_column, self)
        # self.table.setColumnCount(num_column)
        self.table.setHorizontalHeaderLabels([x[0] for x in list_header_attrib])

        rows = self.table.verticalHeader()  # 行
        # rows.setDefaultSectionSize(30)  # 整体行高
        rows.setSectionResizeMode(QHeaderView.Stretch)
        rows.hide()  # 不显示默认序号

        header = self.table.horizontalHeader()  # 列
        # header.setStretchLastSection(True)
        # header.setSectionResizeMode(QHeaderView.Interactive)  # QHeaderView.ResizeToContents

        self.table.cellChanged.connect(self.OnCellChanged)

        self.layout_table.addWidget(self.table)
        return header

    def OnCellChanged(self):
        # crtRow = self.table.currentRow() + 1
        pass


    def load_data_file(self, xml_path):
        ed = DataEditor(xml_path)
        ed.traverse_root(self.load_table_content)
        # ed.traverse_root(lambda elem: self.insert_table_line())

    def load_table_content(self, crtRow, elem):
        self.table.setItem(crtRow, 0, QTableWidgetItem(str(crtRow + 1)))
        self.table.setItem(crtRow, 2, QTableWidgetItem(elem.tag))
        for key, value in elem.items():
            if key == "id":
                self.table.setItem(crtRow, 1, QTableWidgetItem(str(value)))
            elif key == "x":
                self.table.setItem(crtRow, 3, QTableWidgetItem(str(value)))


class DataEditor:
    """ 用于编辑python::dict并将之转换为xml文件 """
    def __init__(self, xml_path):
        self.xml_tree = ET.parse(xml_path)
        self.file_path = xml_path
        self.xml_root = self.xml_tree.getroot()
        self.index = 0


    def _traverse_elem(self, element, CALLBACK):
        for item in element.iter():
            CALLBACK(self.index, item)
            self.index += 1

    def traverse_root(self, CALLBACK):
        self._traverse_elem(self.xml_root, CALLBACK)

    def save_to_file(self, xml_path=None):
        if not xml_path:
            xml_path = self.file_path
        self.xml_tree.write(xml_path)

    def prepare_pane(self):
        param_list = {
            "number": 3,
            "index_start": 1,
            "index_increase": 1,
            "sticky": False,
            # geometry
            "column": 3,  # "row": 1,  # False or 0 while don't need to calculate the geometry
            "total_size": [0, 0],
            "btn_size": [0, 0],
            "margin": [0, 0, 0, 0],
            # tmp param:
            "pane_height": 0,
            # text
            "id_prefix": "",
            "id_suffix": "",
            "text_same_as_content": False,
            "text_prefix": "",
            "text_suffix": "",
            # image
            "imgfile": "",
            "color": "",
            # cmd
            "cmd_type": "key_char",
            "cmd_content": "alpha",  # or "number", "alpha", "ALPHA", or combination list
        }
        return param_list

    def _get_pane_geom_seperator(self, dict_pane):
        nColumn = dict_pane["column"]
        if not nColumn:
            return None
        nRow = int((dict_pane["number"] - 1) / nColumn) + 1

        list_margin = dict_pane["margin"]
        if nColumn > 1:
            _wid = dict_pane["total_size"][0] - list_margin[2] - list_margin[3]
            seperator_x = int((_wid - nColumn * dict_pane["btn_size"][0]) / (nColumn - 1))
        else:
            seperator_x = 0

        if nRow > 1:
            _hei = dict_pane["total_size"][1] - list_margin[0] - list_margin[1]
            seperator_y = int((_hei - nRow * dict_pane["btn_size"][1]) / (nRow - 1))
        else:
            seperator_y = 0
        return seperator_x, seperator_y

    def insert_pane(self, parent, dict_pane):
        if dict_pane["cmd_content"] == "alpha":
            list_content = [chr(x) for x in range(97, 123)]
        elif dict_pane["cmd_content"] == "ALPHA":
            list_content = [chr(x) for x in range(65, 91)]
        elif dict_pane["cmd_content"] == "number":
            list_content = [str(x) for x in range(0, 10)]
        else:
            raise Exception("No such cmd_content[%s]" % dict_pane["cmd_content"])

        if len(list_content) < dict_pane["number"]:
            raise Exception("No so many cmd_contents to fill-in the buttons")

        seperator = self._get_pane_geom_seperator(dict_pane)

        for index in range(dict_pane["number"]):
            str_id = "%s%d%s" % (dict_pane["id_prefix"], 
                                 dict_pane["index_start"] + index * dict_pane["index_increase"],
                                 dict_pane["id_suffix"])
            if dict_pane["text_same_as_content"]:
                str_text = list_content[index].upper()  # upper() ??
            else:
                str_text = "%s%s%s" % (dict_pane["text_prefix"], list_content[index], dict_pane["text_suffix"])

            # calculate geometry
            if seperator:
                size = dict_pane["btn_size"]
                list_margin = dict_pane["margin"]

                index_of_row = index % dict_pane["column"]
                position_x = list_margin[2] + (size[0] + seperator[0]) * index_of_row

                index_of_column = int(index / dict_pane["column"])
                position_y = list_margin[0] + (size[1] + seperator[1]) * index_of_column

                positon = [position_x, position_y]
            else:
                size = None
                position = None

            self.insert_btn(parent, id=str_id, cmd_type=dict_pane["cmd_type"],
                cmd_content=list_content[index], size=size, pos=position, text=str_text,
                imgfile=dict_pane["imgfile"], sticky=True if dict_pane["sticky"] else False)

    def insert_btn(self, parent, id, cmd_type, cmd_content, pos=None, size=None, text=None, 
                    imgfile="", sticky=False):
        sub_elem = ET.SubElement(parent, "btn")
        sub_elem.text = "\n\t\t\t"
        sub_elem.tail = "\n\t\t"
        sub_elem.set("id", id)
        if text:
            sub_elem.set("text", text)
        if sticky:
            sub_elem.set("sticky", "true")
        if pos:
            sub_elem.set("x", str(pos[0]))
            sub_elem.set("y", str(pos[1]))
        if size:
            sub_elem.set("w", str(size[0]))
            sub_elem.set("h", str(size[1]))
        if imgfile:
            sub_elem.set("image", imgfile)
        # 增加按钮功能
        func_elem = ET.SubElement(sub_elem, cmd_type)
        if cmd_type.find("key_") != -1:
            func_elem.set("key", cmd_content)
        elif cmd_type == "shell":
            func_elem.set("cmd", cmd_content)
        elif cmd_type == "plc":
            func_elem.set("input", cmd_content)
        func_elem.tail = "\n\t\t"

    def get_page(self, id=None):
        """ return the elem of the page """
        # id为空时，则返回默认 default_page

        for elem_page in self.xml_root.findall("page"):
            if elem_page.attrib["id"] == id:
                return elem_page


def Test_UDF_Pane_Dict():
        param_list = {
            "number": 26,
            "index_start": 1,
            "index_increase": 2,
            "sticky": False,
            # geometry
            "column": 0,  # "row": 1,
            "geometry": False,
            "total_size": [200, 600],
            "btn_size": [50, 80],
            "margin": [0, 0, 0, 0],
            # tmp param:
            "pane_height": 0,
            # text
            "id_prefix": "path-",
            "id_suffix": "",
            "text_same_as_content": True,
            "text_prefix": "",
            "text_suffix": "",
            # image
            "imgfile": "",
            "color": "",
            # cmd
            "cmd_type": "key_char",
            "cmd_content": "alpha",  # or "number", "alpha", "ALPHA", or combination list
        }
        return param_list


if __name__ == "__main__":
    # app = QApplication(sys.argv)
    # win = EditViewer("../i5os_vkb/information.xml")
    # sys.exit(app.exec_())

    data = DataEditor("../i5os_vkb/vkb.xml")
    elem_page = data.get_page("MP0")
    # data.insert_btn(elem_page, "FA0101", "shell", "echo 'Hello~'", "Here")
    dict_pane = Test_UDF_Pane_Dict()
    data.insert_pane(elem_page, dict_pane)
    data.save_to_file()
